/*
 * Copyright 2007-2010 Jeremy Brown.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author <a href="http://jeremy-brown.com">Jeremy Brown</a>
 */

package groovy.prefuse.demos;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.MouseEvent;
import java.util.Iterator;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.GroupAction;
import prefuse.action.ItemAction;
import prefuse.action.RepaintAction;
import prefuse.action.animate.ColorAnimator;
import prefuse.action.animate.PolarLocationAnimator;
import prefuse.action.animate.QualityControlAnimator;
import prefuse.action.animate.VisibilityAnimator;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.FontAction;
import prefuse.action.layout.CollapsedSubtreeLayout;
import prefuse.action.layout.graph.RadialTreeLayout;
import prefuse.activity.SlowInSlowOutPacer;
import prefuse.controls.ControlAdapter;
import prefuse.controls.DragControl;
import prefuse.controls.FocusControl;
import prefuse.controls.HoverActionControl;
import prefuse.controls.PanControl;
import prefuse.controls.ZoomControl;
import prefuse.controls.ZoomToFitControl;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Table;
import prefuse.data.Tuple;
import prefuse.data.event.TupleSetListener;
import prefuse.data.io.GraphMLReader;
import prefuse.data.query.SearchQueryBinding;
import prefuse.data.search.PrefixSearchTupleSet;
import prefuse.data.search.SearchTupleSet;
import prefuse.data.tuple.DefaultTupleSet;
import prefuse.data.tuple.TupleSet;
import prefuse.render.AbstractShapeRenderer;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.EdgeRenderer;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.util.FontLib;
import prefuse.util.ui.JFastLabel;
import prefuse.util.ui.JSearchPanel;
import prefuse.util.ui.UILib;
import prefuse.visual.VisualItem;
import prefuse.visual.expression.InGroupPredicate;
import prefuse.visual.sort.TreeDepthItemSorter;

import groovy.swing.*
import groovy.prefuse.*
import groovy.prefuse.factory.*

/**
 * Demonstration of a node-link tree viewer
 *
 * @version 1.0
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class RadialGraphView
{
	static PrefuseBuilder swing = new PrefuseBuilder()
	
    public static final String DATA_FILE = "data/socialnet.xml";
    
    private static final String tree = "tree";
    private static final String treeNodes = "tree.nodes";
    private static final String treeEdges = "tree.edges";
    private static final String linear = "linear";
    
    private static LabelRenderer m_nodeRenderer;
    private static EdgeRenderer m_edgeRenderer;


    public static void main(argv) {
        String infile = DATA_FILE;
        String label = "name";
        
        if ( argv.length > 1 ) {
            infile = argv[0];
            label = argv[1];
        }
        
        UILib.setPlatformLookAndFeel();

        swing.build()
		{
        	frame(id:"frame", title:"p r e f u s e  |  r a d i a l g r a p h v i e w", defaultCloseOperation:javax.swing.WindowConstants.EXIT_ON_CLOSE)
			{
				panel(id:"panel")
				{
					borderLayout()

					pfDisplay(id:"display", size:[600,600], constraints:BorderLayout.CENTER)
					{
	        			pfVisualization(id:"vis", interactive:[treeEdges, null, false])
						{
	        				pfGraph(file:DATA_FILE, id:"graph", group:"tree")
	
	        				pfLabelRenderer(textField:"name", forNode:true, id:"labelR", renderType:AbstractShapeRenderer.RENDER_TYPE_FILL, align:Constants.CENTER, roundedCorner:[8,8])
	        				pfEdgeRenderer(predicate:"ingroup('tree.edges')", id:"edgeR")
	        				
							pfColorAction(id:"nodeColor", group:"tree.nodes", field:VisualItem.FILLCOLOR, defaultColor:ColorLib.rgba(255,255,255,0))
							{
	        					pfColorAction(predicate:"_hover", defaultColor:ColorLib.gray(220,230))
	        					pfColorAction(predicate:"ingroup('_search_')", defaultColor:ColorLib.rgb(255,190,190))
	        					pfColorAction(predicate:"ingroup('_focus_')", defaultColor:ColorLib.rgb(198,229,229))
							}

							pfColorAction(id:"textColor", name:"textColor", group:"tree.nodes", field:VisualItem.TEXTCOLOR, defaultColor:ColorLib.gray(0))
							{
								pfColorAction(predicate:"_hover", defaultColor:ColorLib.rgb(255,0,0))
							}

							pfColorAction(id:"edgeColor", group:"tree.edges", field:VisualItem.STROKECOLOR, defaultColor:[200,200,200])
							pfFontAction(id:"fonts", group:"tree.nodes", defaultFont:FontLib.getFont("Tahoma", 10))
							{
								pfFontAction(predicate:"ingroup('_focus_')", defaultFont:FontLib.getFont("Tahoma", 11))
							}
	
	        				pfActionList(id:"recolor", name:"recolor")
	        				{
								pfAction(nodeColor)
								pfAction(textColor)
	        				}
	
	        				pfActionList(id:"repaint", name:"repaint")
							{
								pfAction(recolor)
								pfRepaintAction()
							}
	
	        				pfActionList(id:"animatePaint", name:"animatePaint", duration:400)
							{
								pfColorAnimator(group:"tree.nodes")
								pfRepaintAction()
							}
	
	        				pfRadialTreeLayout(id:"treeLayout", group:"tree", name:"treeLayout" /*, angularBounds:[-Math.PI/2, Math.PI]*/)
	        				pfCollapsedSubtreeLayout(id:"subLayout", group:"tree", name:"subLayout")
	        				
	        				println(vis)
	        				pfTreeRootAction(id:"treeRootAction", name:"treeRootAction", group:"tree", visualization:vis)
	        				/*
        					pfGroupAction(id:"treeRootAction", name:"treeRootAction", group:"tree", 
    							closure:{frac, group->
        					        TupleSet focus = swing.vis.getGroup(Visualization.FOCUS_ITEMS)
        					        if(focus==null || focus.getTupleCount() == 0)
        					        {
        					        	return;
        					        }
        					        Graph g = (Graph)swing.vis.getGroup(group);
        					        Node f = null;
        					        Iterator tuples = focus.tuples();
        					        while(tuples.hasNext() && !g.containsTuple(f=(Node)tuples.next()))
        					        {
        					            f = null
        					        }
        					        if(f == null)
        					        {
        					        	return
        					        }
        					        g.getSpanningTree(f);
        					    }
        					)
        					*/
	        				
	
	        				pfActionList(id:"filter", name:"filter")
							{
	        					//Switch the root of the tree by requesting a new 
	        					//spanning tree at the desired root
	        					pfAction(treeRootAction)
	        					pfAction(fonts)
	        					pfAction(treeLayout)
	        					pfAction(subLayout)
	        					pfAction(textColor)
	        					pfAction(nodeColor)
	        					pfAction(edgeColor)
							}

							pfActionList(id:"animate", name:"animate", duration:1250, pacingFunction:(new SlowInSlowOutPacer()))
							{
								pfQualityControlAnimator()
								pfVisibilityAnimator(group:"tree")
								pfPolarLocationAnimator(group:"tree.nodes", linearGroup:"linear")
								pfColorAnimator(group:"tree.nodes")
								pfRepaintAction()
							}
	        		        
	        		        SearchTupleSet searchSet = new PrefixSearchTupleSet();
	        		        swing.vis.addFocusGroup(Visualization.SEARCH_ITEMS, searchSet);
	        		        searchSet.addTupleSetListener(
	        		        		[tupleSetChanged:{tupleSet, tupleAddList, tupleRemoveList->
	        		                swing.vis.cancel("animatePaint");
	        		                swing.vis.run("recolor");
	        		                swing.vis.run("animatePaint");
	        		            }] as TupleSetListener
	        		        );

		        			pfSearchQueryBinding(id:"searchQ", tupleSet:(Table)vis.getGroup("tree.nodes"), field:"name", searchTupleSet:(SearchTupleSet)vis.getGroup(Visualization.SEARCH_ITEMS))
						}

	        			
	        			pfTreeDepthItemSorter()
						pfDragControl()
						pfZoomToFitControl()
						pfZoomControl()
						pfPanControl()
						pfFocusControl(clicks:1, action:"filter")
						pfHoverActionControl(action:"repaint")

						pfControlListener(
							itemEntered:{item, e->
								if(item.canGetString("name"))
								{
									swing.title.setText(item.getString("name"));
								}
							},
							itemExited:{item, e->
								swing.title.setText(null);
							}
						)
					}

				}
				
				hbox(constraints:BorderLayout.SOUTH)
				{
					hstrut(10)
					pfJFastLabel(id:"title", text:"                 ", preferredSize:[350,20], verticalAlignment:SwingConstants.BOTTOM, border:BorderFactory.createEmptyBorder(3,0,0,0), font:FontLib.getFont("Tahoma", Font.PLAIN, 16))
					hglue()
					pfJSearchPanel(binding:searchQ, showResultCount:true, border:BorderFactory.createEmptyBorder(5,5,4,0), font:FontLib.getFont("Tahoma", Font.PLAIN, 11))
					hstrut(3)
				}
			}
		}

        // maintain a set of items that should be interpolated linearly
        // this isn't absolutely necessary, but makes the animations nicer
        // the PolarLocationAnimator should read this set and act accordingly
        swing.vis.addFocusGroup("linear", new DefaultTupleSet());
        swing.vis.getGroup(Visualization.FOCUS_ITEMS).addTupleSetListener(
               [tupleSetChanged:{tupleSet, tupleAddList, tupleRemoveList->
                    TupleSet linearInterp = swing.vis.getGroup(linear);
                    if(tupleAddList.length < 1) 
                    {
                    	return;
                    }
                    linearInterp.clear();
                    for(Node n = (Node)tupleAddList[0]; n!=null; n=n.getParent())
                    {
                        linearInterp.addTuple(n);
                    }
                }] as TupleSetListener
        );
        
		swing.vis.alwaysRunAfter("filter", "animate");
		swing.vis.run("filter");
		
		Color BACKGROUND = Color.WHITE;
		Color FOREGROUND = Color.DARK_GRAY;
		UILib.setColor(swing.panel, BACKGROUND, FOREGROUND);
		
		
		swing.frame.pack();
		swing.frame.setVisible(true);
    }
}
